import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { AddChargeOrder } from 'src/entities/AddChargeOrder';
import { ChargeOrder } from 'src/entities/ChargeOrder';
import { UserWallet } from 'src/entities/UserWallet';
import { UserWalletLog } from 'src/entities/UserWalletLog';
import { RedisService } from 'src/redis/redis.service';
import { Connection, Repository } from 'typeorm';

@Injectable()
export class ChargeService {
    private startChargeResKey:string;
    private addChargeResKey:string;
    private stopChargeResKey:string;
    private chargingOrderKey:string;

    private profitSettlementQueue:string;

    constructor(
        private connection:Connection,
        private redis:RedisService,
        private configService:ConfigService,
        @InjectRepository(ChargeOrder) protected repository:Repository<ChargeOrder>,
        @InjectRepository(AddChargeOrder) protected addRepository:Repository<AddChargeOrder>
    ) {
        const redisKeys = this.configService.get('redisKey.charge') 
        this.startChargeResKey = redisKeys.startCharge
        this.addChargeResKey = redisKeys.addCharge
        this.stopChargeResKey = redisKeys.stopCharge

        this.chargingOrderKey = redisKeys.chargingOrder

        this.profitSettlementQueue = this.configService.get('redisKey.profitSettlementQueue') 
    }

    private async startTransaction() {
        const queryRunner = this.connection.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction()
        return queryRunner
    }

    private walletUpdate(orderId:number, userId:number, order:ChargeOrder|AddChargeOrder, wallet:UserWallet) {
        let feeAmount = Number(order.feeAmount)
        let feeRewardAmount = Number(order.feeRewardAmount)

        let walletUpdate:{amount? : string, rewardAmount? : string} = {}
        let walletLog:{[key in keyof UserWalletLog]? : UserWalletLog[key]} = {
            userId : userId,
            beforeAmount : wallet.amount,
            beforeRewardAmount : wallet.rewardAmount,
            afterAmount : wallet.amount,
            afterRewardAmount : wallet.rewardAmount,
            relationId : orderId
        }

        if(feeAmount > 0) { //有充值余额支出
            walletUpdate.amount = String(feeAmount + Number(wallet.amount))
            walletLog.amount = order.feeAmount
            walletLog.afterAmount = walletUpdate.amount
        }

        if(feeRewardAmount > 0) { //有赠送余额支出的
            walletUpdate.rewardAmount = String(feeRewardAmount + Number(wallet.rewardAmount))
            walletLog.rewardAmount = order.feeRewardAmount
            walletLog.afterRewardAmount = walletUpdate.rewardAmount
        }

        return {
            walletLog,
            walletUpdate
        }
    }

    

    async startChargeRefund(orderId:number) {
        console.log('开始 startChargeRefund 退款操作')
        const order = await this.repository.findOne({orderId}, {
            select : ['userId', 'feeAmount', 'feeRewardAmount']
        })
        if(!order) return
        const userId = order.userId
        
        let isSuccess = false
        const time = Math.floor((new Date()).getTime() / 1000)
        
        const queryRunner = await this.startTransaction()

        try {
            
            const wallet = await queryRunner.manager.findOne<UserWallet>(UserWallet, {
                where : {userId : userId},
                select : ['amount', 'rewardAmount'],
                lock : {
                    mode : 'pessimistic_read'
                }
            })

            let { walletUpdate, walletLog} = this.walletUpdate(orderId, userId, order, wallet)
            walletLog.type = 3

            {
                //更新钱包
                const { raw } = await queryRunner.manager.update<UserWallet>(UserWallet, { userId }, walletUpdate)
                if(!raw || raw.affectedRows < 1) {
                    console.log('----- 更新钱包 失败 end ---------')
                    return false
                }
            }

            {
                //加入钱包明细
                walletLog.createTime = time
                const { raw } = await queryRunner.manager.insert<UserWalletLog>(UserWalletLog, walletLog)
                if(!raw || raw.affectedRows < 1) {
                    console.log('----- 加入钱包明细 失败 end ---------')

                    return false
                }
            }

            {
                //更新订单状态
                const { raw } = await queryRunner.manager.update<ChargeOrder>(ChargeOrder, { orderId }, {status : 3})
                if(!raw || raw.affectedRows < 1) {
                    console.log('----- 更新订单状态 失败 end ---------')
                    return false
                }
            }

            await queryRunner.commitTransaction()
            console.log('startChargeRefund 退款成功')
            isSuccess = true


        } catch (error) {
            console.log(error)
        }finally {
            if(!isSuccess) {
                await queryRunner.rollbackTransaction()
            }
            queryRunner.release()
        }

        return isSuccess
    }

    async addChargeRefund(orderId:number) {
        console.log('开始 addChargeRefund 增加充电退款')
        const order = await this.addRepository.findOne({orderId}, {select : ['userId', 'feeAmount', 'feeRewardAmount']})
        if(!order) return
        const userId = order.userId
        
        let isSuccess = false
        const time = Math.floor((new Date()).getTime() / 1000)

        const queryRunner = await this.startTransaction()

        try {
            
            const wallet = await queryRunner.manager.findOne<UserWallet>(UserWallet, {
                where : {userId : userId},
                select : ['amount', 'rewardAmount'],
                lock : {
                    mode : 'pessimistic_read'
                }
            })

            let { walletUpdate, walletLog} = this.walletUpdate(orderId, userId, order, wallet)
            walletLog.type = 5

            {
                //更新钱包
                const { raw } = await queryRunner.manager.update<UserWallet>(UserWallet, { userId }, walletUpdate)
                if(!raw || raw.affectedRows < 1) {
                    console.log('----- 更新钱包 失败 end ---------')
                    return false
                }
            }

            {
                //加入钱包明细
                walletLog.createTime = time
                const { raw } = await queryRunner.manager.insert<UserWalletLog>(UserWalletLog, walletLog)
                if(!raw || raw.affectedRows < 1) {
                    console.log('----- 加入钱包明细 失败 end ---------')

                    return false
                }
            }

            {
                //更新订单状态
                const { raw } = await queryRunner.manager.update<AddChargeOrder>(AddChargeOrder, { orderId }, {status : 2})
                if(!raw || raw.affectedRows < 1) {
                    console.log('----- 更新订单状态 失败 end ---------')
                    return false
                }
            }

            await queryRunner.commitTransaction()
            console.log('addChargeRefund 退款成功')
            isSuccess = true


        } catch (error) {
            console.log(error)
        }finally {
            if(!isSuccess) {
                await queryRunner.rollbackTransaction()
            }
            queryRunner.release()
        }

        return isSuccess
    }

    startChargeNotify(orderId:number, chargeResult:number) {
        console.log(' startChargeNotify 操作')
        const key = this.startChargeResKey + orderId
        if(chargeResult == 0) {
            this.redis.store.get(key).then(status => {
                console.log('当前状态', status)
                if(status != null) {
                    this.redis.store.lPush(this.chargingOrderKey + status, String(orderId))
                    this.redis.store.set(key, '1', {EX : 10})
                }
            })
            
        }else {
            this.startChargeRefund(orderId)
            this.redis.del(key) //充电失败的话，直接把状态删除
        }
    }

    async addChargeNotify(chargeOrderId:any, chargeResult:number) {
        console.log(' addChargeNotify 操作')
        let orderId:any = await this.redis.store.get(chargeOrderId)
        if(!orderId) return
        this.redis.store.del(chargeOrderId)
        const key = this.addChargeResKey + orderId
        if(chargeResult == 15) {
            const order = await this.addRepository.findOne({ orderId } , {
                select : ['status', 'userId', 'feeAmount', 'feeRewardAmount', 'chargeAmount']
            })
    
            if(!order) return
    
            const chargeOrder = await this.repository.findOne({orderId : chargeOrderId}, {
                select : ['feeAmount', 'feeRewardAmount', 'chargeAmount', 'status']
            })
    
            if(!chargeOrder || chargeOrder.status != 1) return
    
            const feeAmount = Number(order.feeAmount)
            const feeRewardAmount = Number(order.feeRewardAmount)
    
            let updateData:{[key in keyof ChargeOrder]? : ChargeOrder[key]} = {
                chargeAmount : String(Number(order.chargeAmount) + Number(chargeOrder.chargeAmount))
            }
    
            if(feeAmount > 0) {
                updateData.feeAmount = String(Number(chargeOrder.feeAmount) + feeAmount)
            }
            if(feeRewardAmount > 0) {
                updateData.feeRewardAmount = String(Number(chargeOrder.feeRewardAmount) + feeRewardAmount)
            }
    
            const { affected } = await this.repository.update({orderId : chargeOrderId}, updateData)
            if(affected > 0) {
                this.redis.store.set(key, '1', {EX : 10})
            }
            
        }else {
            this.addChargeRefund(orderId)
            this.redis.del(key) //充电失败的话，直接把状态删除
        }
        
        
    }

    stopChargeNotify(orderId:number, chargeResult:number) {
        console.log(' stopChargeNotify 操作')
        const key = this.stopChargeResKey + orderId
        if(chargeResult == 19) {
            this.redis.store.set(key, '1', {EX : 10})
        }else {
            this.redis.del(key) //停止失败的话，直接把状态删除
        }
    }

    async settlement(settlementData:any) {
        const orderId = settlementData.orderMsgId
        const stopReason = settlementData.paramList[0].stopReason
        let totalTime = settlementData.paramList[0].useTime //总耗时 秒
        const consumeEnergy = settlementData.paramList[0].useEnergy / 1000 //总耗电
        const maxPower = settlementData.paramList[0].maxActivePower / 100 //最大功率
        const times = []
        const order = await this.repository.findOne(orderId, {
            select : ['strategyRules', 'strategyType', 'settleUnit', 'chargeAmount', 'unitPrice', 'settleUnit', 'feeAmount', 'feeRewardAmount', 'userId', 'merchantId', 'subMerchantId', 'profitRate']
        })
        if(!order) return

        const chargeAmount = Number(order.chargeAmount) //订单总金额
        let consumeAmount = 0 //总共用了多少钱
        if(order.settleUnit == 0) { //不退费
            consumeAmount = chargeAmount
        }else {
            const unitPrice = Number(order.unitPrice) //单位价格

            if(order.strategyType == 1) {
                consumeAmount = Math.ceil(totalTime / 60 / order.settleUnit) * order.settleUnit * unitPrice
            }else if(order.strategyType == 2) {
                consumeAmount = Math.ceil(consumeEnergy / order.settleUnit) * order.settleUnit * unitPrice
            }else {
                times.push(settlementData.paramList[0].pwrStage1time || 0, settlementData.paramList[0].pwrStage3time || 0, settlementData.paramList[0].pwrStage3time || 0, settlementData.paramList[0].pwrStage4time || 0)
                let rules:number[] = []
                const settleUnit = order.settleUnit * 60
                times.forEach((t, i) => {
                    if(t) {
                        consumeAmount += Math.ceil(t / settleUnit) *  settleUnit * (order.strategyRules[i].a) / 3600
                    }
                })
            }

            consumeAmount = Number(consumeAmount.toFixed(2))
            if(consumeAmount > chargeAmount) {
                consumeAmount = chargeAmount
            }
        }

        const time = Math.floor((new Date()).getTime() / 3)
        const offsetTime = time - order.createTime
        if(totalTime > offsetTime) {
            totalTime = offsetTime
        }

        let updateData:{[key in keyof ChargeOrder]? : ChargeOrder[key]} = {
            status : 2,
            endTime : time,
            consumeAmount : String(consumeAmount),
            stopReason,
            chargeData : {
                //startChargeTime : startTime,
                //endChargeTime : endTime,
                consumeTime : totalTime, //秒
                consumeEnergy : consumeEnergy,
                maxPower : maxPower,
                times : times
            }         
        }

        let refundAmount = chargeAmount - consumeAmount
        let isSuccess = false
        if(refundAmount >= 0.01) { //有退款
            const userId = order.userId
            
            const orderFeeRewardAmount = Number(order.feeRewardAmount)
            const queryRunner = await this.startTransaction()

            try {
                const wallet = await queryRunner.manager.findOne<UserWallet>(UserWallet, {
                    where : {userId : userId},
                    select : ['amount', 'rewardAmount'],
                    lock : {
                        mode : 'pessimistic_read'
                    }
                })

                
                const rechargeBalance = Number(wallet.amount) //充值的余额
                const rewardBalance = Number(wallet.rewardAmount) //赠送的余额
                let walletUpdate:{amount? : string, rewardAmount? : string} = {}
                let walletLog:{[key in keyof UserWalletLog]? : UserWalletLog[key]} = {
                    userId,
                    beforeAmount : wallet.amount,
                    beforeRewardAmount : wallet.rewardAmount,
                    afterAmount : wallet.amount,
                    afterRewardAmount : wallet.rewardAmount,
                    type : 5,
                    relationId : orderId,
                }

                if(orderFeeRewardAmount >= refundAmount) { //全部退还到赠送余额
                    walletUpdate.rewardAmount = String(refundAmount + rewardBalance)

                    walletLog.rewardAmount = String(refundAmount)
                    walletLog.afterRewardAmount = walletUpdate.rewardAmount
                    
                }else {
                    const offset = refundAmount - orderFeeRewardAmount //这是要退给充值余额的钱

                    walletUpdate.amount = String(offset + rechargeBalance)
                    walletLog.amount = String(offset)
                    walletLog.afterAmount = walletUpdate.amount

                    if(orderFeeRewardAmount > 0) {
                        walletUpdate.rewardAmount = String(rewardBalance + orderFeeRewardAmount)
                        walletLog.rewardAmount = order.feeRewardAmount
                        walletLog.afterRewardAmount = walletUpdate.rewardAmount
                    }
                }

                {
                    //更新钱包
                    const { affected } = await queryRunner.manager.update<UserWallet>(UserWallet, { userId }, walletUpdate)
                    if(!affected) {
                        return
                    }
                }

                {
                    //加入钱包明细
                    walletLog.createTime = time
                    const { raw } = await queryRunner.manager.insert<UserWalletLog>(UserWalletLog, walletLog)
                    if(!raw || raw.affectedRows < 1) {
                        return
                    }
                }

                {
                    //更新订单
                    const { affected } = await queryRunner.manager.update<ChargeOrder>(ChargeOrder, { orderId }, updateData)
                    if(!affected) {
                        return
                    }
                }
                await queryRunner.commitTransaction()
                isSuccess = true

            } catch (error) {
                console.log('结算退费 error')
                console.log(error)
            } finally {
                if(!isSuccess) await queryRunner.rollbackTransaction()
                queryRunner.release()
            }

        }else {
            await this.repository.update(orderId, updateData)
            isSuccess = true
        }

        if((consumeAmount > 0 || totalTime > 0 || consumeEnergy > 0) && isSuccess) {
            this.redis.store.lPush(this.profitSettlementQueue, JSON.stringify({
                time : time,
                merchantId : order.merchantId,
                subMerchantId : order.subMerchantId,
                profitRate : order.profitRate,
                amount : consumeAmount,
                consumeEnergy,
                totalTime
            }))
        }
        
    }
}
